Write a custom CUDA kernel to optimize `xIELU` using `float64` (double) precision.

Formula:
  f(x) = alpha_p * x^2 + 0.5*x                     if x >= 0
  f(x) = alpha_n * (exp(x) - 1) - alpha_n*x + 0.5*x if x < 0

Problem Analysis:
1. Precision Issues with float32: The `exp(x) - 1` term suffers from catastrophic cancellation near zero. While `expm1` helps, small discrepancies between different math library implementations can cause `allclose` to fail.
2. Memory Bottleneck: The operation is memory-bound.

Optimization Strategy: Fused Element-wise Kernel with Double Precision

1. Data Type: All computations are performed in `double` to guarantee accuracy.

2. Vectorized Loads (double2): Use `double2` to load 128 bits (2 double elements) per memory transaction, maintaining memory efficiency.

3. Fused Stable Math (in double):
   - For `x < 0`, use `expm1(x)` which is the `double` precision version of `expm1f`.

4. One-Pass: Fuse all logic into a single read-compute-write kernel.
  
Here's an example to show you the syntax of inline embedding custom CUDA operators in torch: The example given architecture is:   
  
```python
import torch
import torch.nn as nn

BATCH_SIZE = 4096
HIDDEN_DIM = 8192
SHAPE = (BATCH_SIZE, HIDDEN_DIM)

ALPHA_P_VAL = 0.8
ALPHA_N_VAL = 0.8

DTYPE = torch.float64

class xIELU(nn.Module):
    """
    xIELU from "Deriving Activation Functions via Integration"
    https://arxiv.org/html/2411.13010v1
    Formula:
      f(x) = alpha_p * x^2 + 0.5*x                     if x >= 0
      f(x) = alpha_n * (exp(x) - 1) - alpha_n*x + 0.5*x if x < 0
    """
    def __init__(self, alpha_p=0.8, alpha_n=0.8):
        super(xIELU, self).__init__()
        self.alpha_p = alpha_p
        self.alpha_n = alpha_n

    def forward(self, x: torch.Tensor) -> torch::Tensor:
        # Positive part: ap * x^2 + 0.5x
        pos_part = self.alpha_p * torch.pow(x, 2) + 0.5 * x
        
        # Negative part: an * (e^x - 1) - an*x + 0.5x
        neg_part = self.alpha_n * torch.expm1(x) - self.alpha_n * x + 0.5 * x
        
        return torch.where(x >= 0, pos_part, neg_part)

class Model(nn.Module):
    def __init__(self, alpha_p=0.8, alpha_n=0.8):
        super(Model, self).__init__()
        self.act = xIELU(alpha_p=alpha_p, alpha_n=alpha_n)
    
    def forward(self, x):
        return self.act(x)

def get_inputs():
    input_tensor = torch.randn(SHAPE, dtype=DTYPE)
    return [input_tensor.contiguous()]

def get_init_inputs():
    return [ALPHA_P_VAL, ALPHA_N_VAL]